Making Massive Apps Mini: Google's Closure Compiler

Nate Young

April 5, 2014

Hello Whole Program Optimization

hello.js

goog.provide('example');

goog.require('goog.dom');

example.sayHello = function(message) {
    goog.dom.getElement('hello').innerHTML = 'Hello ' + message;
};

hello.html

<html>
  <head>
    <title>Example: Hello World</title>
  </head>
  <body>
    <div id="hello"></div>

    <script src="../closure-library/closure/goog/base.js"></script>
    <script src="hello.js"></script>
    <script>
      example.sayHello('TCCC 16!')
    </script>

  </body>
</html>

What the...

hello.html again

goog.provide('example');

goog.require('goog.dom');

example.sayHello = function(message) {
    goog.dom.getElement('hello').innerHTML = 'Hello ' + message;
};

example.sayHello('TCCC 16!');

compilation

$ ./compile

hello.html again

<html>
  <head>
    <title>Example: Hello World</title>
    <style>
      #hello { font-size: 500% }
    </style>
  </head>
  <body>
    <div id="hello"></div>

    <script src="hello-compiled.js"></script>

  </body>
</html>

What the...

$ cat compile

#!/bin/bash

python ../closure-library/closure/bin/calcdeps.py \
  --path ../closure-library \
  --input hello.js \
  --compiler_jar ../closure-compiler/build/compiler.jar \
  --output_mode compiled \
  --compiler_flags="--compilation_level=ADVANCED_OPTIMIZATIONS" \
  --compiler_flags="--define=goog.userAgent.ASSUME_WEBKIT=true" \
  > hello-compiled.js

No, really, what the...

$ ls -lah

drwxr-xr-x   6 nate.young  2023218408   204B Apr  4 21:05 .
drwxr-xr-x  11 nate.young  2023218408   374B Apr  4 20:54 ..
-rwxr-xr-x   1 nate.young  2023218408   289B Apr  4 21:04 compile
-rw-r--r--   1 nate.young  2023218408   1.4K Apr  4 21:05 hello-compiled.js
-rw-r--r--   1 nate.young  2023218408   226B Apr  4 21:01 hello.html
-rw-r--r--   1 nate.young  2023218408   191B Apr  4 21:05 hello.js

Last Chance

$ $EDITOR compile

...
  --compiler_flags="--define=goog.userAgent.ASSUME_WEBKIT=true" \
  > hello-compiled.js

Last Chance

$ ls -lah

-rw-r--r--   1 nate.young  2023218408    61B Apr  4 21:11 hello-compiled.js

Last Chance

$ cat hello-compiled.js

document.getElementById("hello").innerHTML="Hello TCCC 16!";

What is Closure?

What is Closure?

What is Closure?

What is Closure?

A compiler that turns code into JavaScript, when that code is, well, JavaScript

Motivation

Protip

Compilation is not exactly conducive to flow

Compiler Modes

Example: Compiling Existing Codebases

Whitespace Only

Simple Optimizations

Local variable renaming

function translatePos(blocks, top_offset, left_offset, grid_height) {
    for(var i = 0; i < blocks.length; i++) {
        var blk = blocks[i];
        var x = blk.x;
        var y = blk.y;
        blk.style.top = glbl_width * (grid_height - 1 - y) + top_offset;
        blk.style.left = glbl_width * x + left_offset + border_width;
    }
}

Local variable renaming

function translatePos(a, b, c, d) {
  for(var f = 0;f < a.length;f++) {
    var e = a[f], g = e.x;
    e.style.top = y * (d - 1 - e.y) + b;
    e.style.left = y * g + c + z
  }
}

Local variable renaming

Inlining / Constant Folding

BANG = '!';

example.sayHello = function(message) {
    goog.dom.getElement('hello').innerHTML = 'Hello ' + message + BANG;
};

example.sayHello('TCCC 16');

Inlining / Constant Folding



example.sayHello = function(message) {
    goog.dom.getElement('hello').innerHTML = 'Hello ' + message + '!';
};

example.sayHello('TCCC 16');

Inlining / Constant Folding



                                      
    goog.dom.getElement('hello').innerHTML = 'Hello ' + 'TCCC 16' + '!';
  

                            

Inlining / Constant Folding



                                      
    goog.dom.getElement('hello').innerHTML = 'Hello TCCC 16!';
  

                            

Inlining / Constant Folding

"Advanced" Optimizations

Property flattening

really.long.dotted.namespace = function(x) { ... }

Property flattening

really$long$dotted$namespace = function(x) { ... }

Property flattening

really$long$dotted$namespace = function(x) { ... }

Property flattening

goog.require('goog.string');

postMessage = function(gs, message) {
  gs.htmlEscape(message) + '<hr>' + gs.htmlEscape(message)
}

postMessage(goog.string, 'I like <script> tags');

example from Closure: The Definitive Guide by Michael Bolin

Property flattening

goog.require('goog.string');

postMessage = function(message) {
  goog.string.htmlEscape(message) + '<hr>' + goog.string.htmlEscape(message)
}

postMessage('I like <script> tags');

example from Closure: The Definitive Guide by Michael Bolin

Property flattening

Property renaming

example.Greeter.prototype.sayHello = function(subject) { ... }

Property renaming

example$Greeter.prototype.a = function(subject) { ... }

Property renaming

example$Greeter.prototype.a = function(subject) { ... }

Property renaming

var choices = {
  carnivore: function() { alert("I'll have the steak!"); }
  herbivore: function() { alert("I'll have the creamed spinach!"); }
};

var pickOne = function(choice) {
  choices[choice]();
};

pickOne('carnivore');

example from Closure: The Definitive Guide by Michael Bolin

Property renaming

var a = {
  a: function() { alert("I'll have the steak!"); }
  b: function() { alert("I'll have the creamed spinach!"); }
};

var f = function(c) {
  a[c]();
};

f('carnivore');

example from Closure: The Definitive Guide by Michael Bolin

Property renaming

var choices = {
  'carnivore': function() { alert("I'll have the steak!"); }
  'herbivore': function() { alert("I'll have the creamed spinach!"); }
};

var pickOne = function(choice) {
  choices[choice]();
};

pickOne('carnivore');

example from Closure: The Definitive Guide by Michael Bolin

Property renaming

var a = {
  'carnivore': function() { alert("I'll have the steak!"); }
  'herbivore': function() { alert("I'll have the creamed spinach!"); }
};

var f = function(c) {
  a[c]();
};

f('carnivore');

example from Closure: The Definitive Guide by Michael Bolin

Property renaming

var choices = {
  carnivore: function() { alert("I'll have the steak!"); }
  herbivore: function() { alert("I'll have the creamed spinach!"); }
};

var pickOne = function(choicefn) {
  choicefn();
};

pickOne(choices.carnivore);

example from Closure: The Definitive Guide by Michael Bolin

Surviving Renaming

Dead-code elimination